/*
 * Created on Mar 3, 2010
 */
package com.mgmaps.app;

import java.io.*;
import java.util.Timer;
import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreNotFoundException;
import com.mgmaps.net.DownloadHandler;
import com.mgmaps.net.Downloader;
import com.mgmaps.platform.IPlatformHandler;
import com.mgmaps.utils.AsyncCalls;
import com.mgmaps.utils.ExtLogger;

public class APPSettings {
  public static String version;
  public static volatile boolean versionUpdate = false;

  // watermark
  public final static String AUTHOR = "Cristian Streng <cristian.streng@mgmaps.com>";
  private static final String DEFAULT_IMEI = "769866062107451";
  
  public static boolean gpsEnabled = true;
  public static boolean gpsSyncEnabled = true;
  public static int gpsCostAllowed = 1;
  public static String gpsDevice = "Internal";
  public static int gpsFixCutoff;
  
  public static boolean hasLAPI;
  public static boolean hasBT;
  public static boolean hasJSR75FConn;
  public static boolean hasMMAPI;
  
  public static int propsVersionOld;
  protected static int propsVersion;
  public static String rsName;
  public static String appURL;
  public static boolean firstTime = true;
  
  public static double savePointX;
  public static double savePointY;
  public static int savePointZ;
  public static int savePointT;
  
  // flags
  public static boolean featureMap;
  public static boolean featureGPS;
  public static boolean featureOnline;
  public static boolean featureBacklight;
  
  // default zoom for search results
  public static int defaultSearchZoom;
  
  // common timer
  public static Timer timer;
  
  // common downloader
  public static Downloader dl;

  // language
  public static String language = "en_us";
  
  public static final String[] httpExtensions = { "", ";deviceside=false",
    ";deviceside=false;ConnectionType=mds-public", ";deviceside=true",
    ";deviceside=true;interface=wifi",
    ";WapGatewayAPN=proxy;WapGatewayIP=10.250.250.250", "Custom" };
  public static String httpExtension = "";
  
  private static final String[] imeiPropNames = {
    "phone.imei", "com.nokia.IMEI", "com.nokia.mid.imei",  // Nokia
    "com.sonyericsson.imei", "com.sonyericsson.IMEI",  // Sony-Ericsson
    "com.samsung.imei",  // Samsung
    "IMEI", "com.motorola.IMEI",  // Motorola
    "com.siemens.IMEI", "com.siemens.imei", "com.siemens.mp.imei",  // Siemens
    "device.imei", "com.imei",  // Generic
  };
  
  // detected
  public static boolean isBlackberry;
  public static boolean isNokia;
  public static boolean isNokiaTouch;
  public static IPlatformHandler platformHandler;

  public static boolean backlightOn = true;
  
  public static String deviceType;
  public static String deviceID;
  
  public static void initialize() {
    detect();
    
    // timer
    timer = new Timer();
    
    // asyncCalls
    AsyncCalls.instance.start();
    
    // set gpsDevice
    // if (hasLAPI)
    //   gpsDevice = "Internal";

    // gpsCostAllowed
    if (isBlackberry) {
      String devName = platformHandler.getDeviceName();
      String carrier = platformHandler.getCarrierName();
      if (carrier == null)
        carrier = "";
        
      if (featureOnline)
        httpExtension = platformHandler.getBlackberryHTTPExtension();
        
      // Blackberry Storm, disable cost allowed
      if (devName != null && (devName.startsWith("95") || devName.startsWith("96")))
         gpsCostAllowed = 0;
    }
  }
  
  public static void initializeDownloader(DownloadHandler defaultHandler) {
    if (!featureOnline)
      return;
    
    if (defaultHandler != null)
      Downloader.defaultHandler = defaultHandler;
    dl = new Downloader();
    dl.start();
  }
  
  public static void detect() {
    // String prop = System.getProperty("microedition.platform");
    // isBlackberry = prop.startsWith("RIM");
    
    // detect blackberry
    try {
      Class.forName("net.rim.device.api.system.DeviceInfo");
      isBlackberry = true;
    }
    catch (Exception ex) {
      isBlackberry = false;
    }
    
    // detect Nokia/SE UI
    try {
      Class.forName("com.nokia.mid.ui.DeviceControl");
      isNokia = true;
    }
    catch (Exception ex) {
      isNokia = false;
    }
    
    platformHandler = null;
    if (APPSettings.isBlackberry)
      try {
        platformHandler = (IPlatformHandler) Class.forName("com.mgmaps.platform.PlatformBB").newInstance();
      }
      catch (Exception ex) {
        ExtLogger.handle(ex, true, "Can't instantiate platform handler");
      }

    else if (APPSettings.isNokia)
      try {
        platformHandler = (IPlatformHandler) Class.forName("com.mgmaps.platform.PlatformNokia").newInstance();
      }
      catch (Exception ex) {
        ExtLogger.handle(ex, true, "Can't instantiate platform handler");
      }
    
    // detect JSR-82 (bluetooth support) - works with MIDP 1.0 too
    try {
      Class.forName("javax.bluetooth.LocalDevice");
      hasBT = true;
    }
    catch (Throwable t) {
      hasBT = false;
    }
    
    // detect JSR-179 (location api) - only with MIDP 2.0
    String prop = System.getProperty("microedition.location.version");
    hasLAPI = (prop != null && prop.length() > 0);
    if (hasLAPI) {
      try {
        Class.forName("javax.microedition.location.Location");
      }
      catch (Throwable t) {
        hasLAPI = false;
      }
    }
    
    // detect JSR-75 (file connection API)
    prop = System.getProperty("microedition.io.file.FileConnection.version");
    hasJSR75FConn = (prop != null && prop.length() > 0);
    if (hasJSR75FConn) {
      try {
        Class.forName("javax.microedition.io.file.FileConnection");
      }
      catch (Throwable t) {
        hasJSR75FConn = false;
      }
    }

    // detect JSR-135 (MMAPI)
    try {
      Class.forName("javax.microedition.media.Player");
      hasMMAPI = true;
    }
    catch (Throwable t) {
      hasMMAPI = false;
    }
    
    identify();
  }
  
  public static void identify() {
    if (isBlackberry)
      deviceID = platformHandler.getDeviceID();
    if (deviceID == null)
      deviceID = getIMEI();
    if (deviceID == null)
      deviceID = DEFAULT_IMEI;
    if (platformHandler != null)
      deviceType = platformHandler.getDeviceName();
    if (deviceType == null)
      try {
        deviceType = System.getProperty("microedition.platform").replace('/', '-');
      }
      catch (Exception ex) {
        deviceType = "unknown";
      }
    if (isBlackberry) {
      String cn = platformHandler.getCarrierName();
      deviceType += "-" + (cn == null ? "" : cn);
      deviceType = "BB-" + deviceType;
    }
    else
      deviceType = "J2ME-" + deviceType;
    deviceType += "-" + version;
    
    isNokiaTouch = isNokia && (deviceType.indexOf("sw_platform_version=5") >= 0);
    
    // This code causes the BT force startup.
    /*
     * if(hasBT){ try { LocalDevice local = LocalDevice.getLocalDevice(); phoneGUID =
     * local.getBluetoothAddress(); return; } catch (BluetoothStateException e) { // continue to
     * next attempt } }
     */
  }
  
  /**
   * Notify that the app is exiting.
   */
  public static void notifyExit() {
    AsyncCalls.instance.notifyExit();
    timer.cancel();
    if (dl != null)
      dl.notifyExit();
  }
  
  private static String getIMEI() {
    for (int i=0; i<imeiPropNames.length; i++) {
      // get imei
      String prop = System.getProperty(imeiPropNames[i]);
      if (prop != null && (prop.length() > 0)) {
        final StringBuffer imeiTryBuffer = new StringBuffer();
        char[] propCharArray = prop.toCharArray();
        // extract digits
        for (int j=0; j<prop.length(); j++)
          if (propCharArray[j] >= '0' && propCharArray[j] <= '9')
            imeiTryBuffer.append(j);
        final String imeiTry = imeiTryBuffer.toString();
        if (imeiTry.length() == 15 || imeiTry.length() == 17)
          return imeiTry;
      }
    }
    
    // no IMEI
    return null;
  }
  
  protected static RecordStore openRecordStore() {
    RecordStore rs = null;
    try {
      rs = RecordStore.openRecordStore(rsName, true);
    }
    catch (RecordStoreException rse) {
      ExtLogger.handle(rse, true, "Error opening record store");
    }
    
    return rs;
  }
  
  protected static void closeRecordStore(final RecordStore rs) {
    if (rs != null) {
      try {
        rs.closeRecordStore();
      }
      catch (RecordStoreException rse) {
      }
    }
  }
  
  protected static void emptyRecordStore() {
    // delete
    try {
      RecordStore.deleteRecordStore(rsName);
    }
    catch (RecordStoreNotFoundException rsnfe) {
      // ignore not found
    }
    catch (Exception ex) {
      ExtLogger.handle(ex, false, "Error deleting record store");
    }
  }
  
  /**
   * Write a record store entry.
   * 
   * @param dos data output stream
   * @param bos byte array output stream
   * @param i entry index
   */
  protected static void writeRSEntry(final RecordStore rs, final DataOutputStream dos, final ByteArrayOutputStream bos, final int i) throws RecordStoreException, IOException {
    dos.flush();
    byte[] data = bos.toByteArray();
    try {
      rs.setRecord(i, data, 0, data.length);
    }
    catch (InvalidRecordIDException ex) {
      // Logger.debug("Caught InvalidRecordIDException, adding record");
      rs.addRecord(data, 0, data.length);
    }
    bos.reset();
  }
  
  /**
   * Load settings.
   * @param rs RecordStore to load from
   * @throws Exception if an exception occurs
   */
  public static void load(final RecordStore rs) throws Exception {
    byte[] data = null;
    DataInputStream dis = null;
    
    // version (1)
    try {
      data = rs.getRecord(1);
      dis = new DataInputStream(new ByteArrayInputStream(data));
      propsVersionOld = dis.readInt();
      firstTime = false;
    }
    finally {
      if (dis != null)
        dis.close();
    }
      
    // generic settings (2)
    dis = null;
    try {
      data = rs.getRecord(2);
      dis = new DataInputStream(new ByteArrayInputStream(data));
      language = dis.readUTF();
      if (featureOnline)
        httpExtension = dis.readUTF();
      if (featureBacklight)
        backlightOn = dis.readBoolean();

      if (featureGPS) {
        gpsDevice = dis.readUTF();
        gpsEnabled = dis.readBoolean();
        gpsSyncEnabled = dis.readBoolean();
        gpsCostAllowed = dis.readInt();
      }
        
      if (featureMap) {
        savePointX = dis.readDouble();
        savePointY = dis.readDouble();
        savePointZ = dis.readInt();
        savePointT = dis.readInt();
      }
    }
    finally {
      if (dis != null)
        dis.close();
      if (httpExtension == null)
        httpExtension = "";
    }
  }
  
  /**
   * Save settings.
   * @param rs RecordStore to save to
   * @throws Exception if an exception occurs
   */
  public static void save(final RecordStore rs) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(bos);
      
    // version (1)
    dos.writeInt(propsVersion);
    writeRSEntry(rs, dos, bos, 1);
      
    // generic settings (2)
    dos.writeUTF(language);
    if (featureOnline)
      dos.writeUTF(httpExtension);
    if (featureBacklight)
      dos.writeBoolean(backlightOn);
    if (featureGPS) {
      dos.writeUTF(gpsDevice);
      dos.writeBoolean(gpsEnabled);
      dos.writeBoolean(gpsSyncEnabled);
      dos.writeInt(gpsCostAllowed);
    }
    if (featureMap) {
      dos.writeDouble(savePointX);
      dos.writeDouble(savePointY);
      dos.writeInt(savePointZ);
      dos.writeInt(savePointT);
    }
    writeRSEntry(rs, dos, bos, 2);
  }
  
  /**
   * Get the lang part of the language name.
   * @return language (2 chars)
   */
  public static String getLangNameShort() {
    return language.substring(0, 2);
  }
  
  /**
   * Get the country part of the language name.
   * @return country (2 chars)
   */
  public static String getLangNameCountry() {
    return language.substring(3);
  }
}
